#include "sdvglwidget.h"

#include "shader.h"
#include "model.h"

#include <QSurfaceFormat>
#include <QOpenGLContext>
#include <QMouseEvent>
#include <QKeyEvent>

class QMouseEvent;
class QWheelEvent;
class QTimerEvent;

struct draw_type DT;

struct move_type MT;

struct texture_state TS;

struct animation_state AS;

struct light_type LT;

extern const char* vertexShaderCodeNew;
extern const char* fragmentShaderCodeNew;

unsigned int SCR_WIDTH = 800;
unsigned int SCR_HEIGHT = 600;

// lamp data
float vertices_lamp[] = {
    // positions
    -0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    -0.5f,  0.5f, -0.5f,
    -0.5f, -0.5f, -0.5f,

    -0.5f, -0.5f,  0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,
    -0.5f, -0.5f,  0.5f,

    -0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f, -0.5f,
    -0.5f, -0.5f, -0.5f,
    -0.5f, -0.5f, -0.5f,
    -0.5f, -0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,

    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,

    -0.5f, -0.5f, -0.5f,
    0.5f, -0.5f, -0.5f,
    0.5f, -0.5f,  0.5f,
    0.5f, -0.5f,  0.5f,
    -0.5f, -0.5f,  0.5f,
    -0.5f, -0.5f, -0.5f,

    -0.5f,  0.5f, -0.5f,
    0.5f,  0.5f, -0.5f,
    0.5f,  0.5f,  0.5f,
    0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f,  0.5f,
    -0.5f,  0.5f, -0.5f
};

sdvGLWidget::sdvGLWidget(QWidget *parent):
    camera(QVector3D(0.0f, 0.0f, 3.0f), QVector3D(0.0f, 1.0f, 0.0f))
{
    Q_UNUSED(parent);

    view.setToIdentity();
    model.setToIdentity();
    //model.translate(QVector3D(0.0f, -0.5f, 0.0f));  // translate it down so it's at the center of the scene
    //model.rotate(20.0f, QVector3D(1.0f, 1.0f, 0.0f));
    //model.scale(QVector3D(0.02f, 0.02f, 0.02f));	// it's a bit too big for our scene, so scale it down
}

sdvGLWidget::~sdvGLWidget()
{
    delete ourShader;
    delete ourModel1;
    delete ourModel2;
    delete ourModel3;
    delete ourModel4;
}

void sdvGLWidget::loadModelClarkBoxcube()
{
    ourModel = ourModel1;
    camera.Position = ourModel1->cameraPos;
}

void sdvGLWidget::loadModelClarkCube()
{
    ourModel = ourModel2;
    camera.Position = ourModel2->cameraPos;
}

void sdvGLWidget::loadModelLoopCone()
{
    ourModel = ourModel3;
    camera.Position = ourModel3->cameraPos;
}

void sdvGLWidget::loadModeLoopZ()
{
    ourModel = ourModel4;
    camera.Position = ourModel4->cameraPos;
}

void sdvGLWidget::subdivision()
{
    //
    ourModel->subdivision();
}

void sdvGLWidget::original()
{
    ourModel->original();
}

void sdvGLWidget::initializeGL()
{
    initializeOpenGLFunctions();

    // 新建着色器
    ourShader = new Shader("1.model_loading.vs", "1.model_loading.fs");

    // loop : 对三角形网格的细分
    ourModel4 = new Model("loop_z.off");
    ourModel4->cameraPos = QVector3D(0.0f, 0.0f, 7.0f);
    ourModel4->clarkLoop = 2;

    // loop : 对三角形网格的细分
    ourModel3 = new Model("loop_cone.off");
    ourModel3->cameraPos = QVector3D(0.0f, 0.0f, 2.5f);
    ourModel3->clarkLoop = 2;

    // clark : 对四边形网格的细分
    ourModel1 = new Model("clark_boxcube.off");
    ourModel1->cameraPos = QVector3D(0.0f, 0.0f, 10.0f);
    ourModel1->clarkLoop = 1;

    // clark : 对四边形网格的细分
    ourModel2 = new Model("clark_cube.off");
    ourModel2->cameraPos = QVector3D(0.0f, 0.0f, 4.5f);
    ourModel2->clarkLoop = 1;

    loadModelClarkBoxcube();
    //loadModelClarkCube();
    //loadModelLoopCone();
    //loadModeLoopZ();

//    // 灯的着色器
//    lampShader = new Shader("6.lamp.vs", "6.lamp.fs");

//    glGenVertexArrays(1, &lightVAO);
//    glGenBuffers(1, &lightVBO);

//    // bind data, position attribute
//    glBindVertexArray(lightVAO);
//    glBindBuffer(GL_ARRAY_BUFFER, lightVBO);
//    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_lamp), vertices_lamp, GL_STATIC_DRAW);
//    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
//    glEnableVertexAttribArray(0);
//    glBindVertexArray(0);

    // fixed light position
    lightPos.setX(1.5f);
    lightPos.setY(1.3f);
    lightPos.setZ(2.5f);
}

void sdvGLWidget::paintGL()
{
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_ACCUM_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    //glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    //glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    glEnable(GL_DEPTH_TEST);

//    // lamp
//    if (LT.l_type == 1) {
//        lampShader->use();
//        projection.setToIdentity();
//        projection.perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
//        lampShader->setMat4("projection", projection);
//        view = camera.GetViewMatrix();
//        lampShader->setMat4("view", view);
//        QMatrix4x4 m;
//        m.setToIdentity();
//        m.translate(lightPos);
//        m.scale(0.2f); // a smaller cube: lamp
//        lampShader->setMat4("model", m);
//        glBindVertexArray(lightVAO);
//        glPolygonMode(GL_FRONT, GL_FILL);
//        glPolygonMode(GL_BACK, GL_FILL);
//        glDrawArrays(GL_TRIANGLES, 0, 36);
//    }

    // don't forget to enable shader before setting uniforms
    ourShader->use();

    // view/projection transformations
    projection.setToIdentity();
    projection.perspective(camera.Zoom, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
    view = camera.GetViewMatrix();
    ourShader->setMat4("projection", projection);
    ourShader->setMat4("view", view);

    if (AS.isAnimating == 1) {
        float angle = 0.5f;
        model.rotate(angle, QVector3D(0.0f, 1.0f, 0.0f));
    }

    ourModel->drawType = DT.type;

    ourModel->lampType = LT.l_type;
    ourModel->lightPos = lightPos;
    ourModel->cameraPos = camera.Position;
    ourModel->cameraFront = camera.Front;

    ourModel->texEnable = TS.texEnable;
    ourModel->orgColor = TS.orgColor;

    // render the loaded model
    ourShader->setMat4("model", model);
    ourModel->Draw(*ourShader);
}

void sdvGLWidget::resizeGL(int w, int h)
{
    SCR_WIDTH = w;
    SCR_HEIGHT = h;
    glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT);
}

void sdvGLWidget::mousePressEvent(QMouseEvent *event) {
    MT.lastX = event->x();
    MT.lastY = event->y();
}

void sdvGLWidget::mouseMoveEvent(QMouseEvent *event)
{
    if (MT.move_type == 1) {
        MT.xoffset = event->x() - MT.lastX;
        MT.yoffset = MT.lastY - event->y(); // reversed since y-coordinates go from bottom to top
        MT.lastX = event->x();
        MT.lastY = event->y();
        camera.ProcessMouseMovement(MT.xoffset, MT.yoffset);
    } else if (MT.move_type == 2) {
        float sensitivity = 0.2f;
        MT.r_xoffset += sensitivity * (event->x() - MT.lastX);
        MT.r_yoffset += sensitivity * (MT.lastY - event->y()); // reversed since y-coordinates go from bottom to top
        MT.lastX = event->x();
        MT.lastY = event->y();
        camera.ProcessRotation(MT.r_xoffset, MT.r_yoffset);
    }
    update();
}

void sdvGLWidget::mouseReleaseEvent(QMouseEvent *event) {
    Q_UNUSED(event);
    update();
}

void sdvGLWidget::wheelEvent(QWheelEvent *event) {
    // 缩放功能
    camera.ProcessMouseScroll(event->delta() * 0.012);
    update();
}

void sdvGLWidget::keyPressEvent(QKeyEvent *event)
{
    float cameraSpeed = 0.45;
    if (event->key() == Qt::Key_W)
        camera.Position += cameraSpeed * camera.Front.normalized();
    if (event->key() == Qt::Key_S)
        camera.Position -= cameraSpeed * camera.Front.normalized();
    if (event->key() == Qt::Key_A)
        camera.Position -= QVector3D::crossProduct(camera.Front, camera.Up).normalized() * cameraSpeed;
    if (event->key() == Qt::Key_D)
        camera.Position += QVector3D::crossProduct(camera.Front, camera.Up).normalized() * cameraSpeed;
    update();
}

void sdvGLWidget::initFpsCamera()
{
    camera.initFpsCamera();
}

void sdvGLWidget::timerEvent(QTimerEvent *event)
{
    // for animation
    Q_UNUSED(event);
    update();
}
